package com.example.test1.log.aspect;

import com.example.test1.log.annotation.LoggerAnnotation;
import com.example.test1.log.bean.ExceptionOperLog;
import com.example.test1.log.bean.OperationLog;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.bitwalker.useragentutils.UserAgent;
import jakarta.servlet.http.HttpServletRequest;
import jdk.dynalink.Operation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.lang.reflect.Method;
import java.util.Stack;
@Component
@Aspect
@Slf4j
public class LoggerAspect {
    private static final ObjectMapper mapper = new ObjectMapper();
    @Pointcut("@annotation(com.example.test1.log.annotation.LoggerAnnotation)")
    public void logger(){
    }
    private String writeValueAsSting(Object object){
        String json = null;
        try {
            json = mapper.writeValueAsString(object);
        }catch (JsonProcessingException e){
            log.error("object covert json exception",e);
        }
        return json;
    }
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements){
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements){
            strbuff.append(stet+"\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }
    private OperationLog buildOperationLog(JoinPoint joinPoint,Object ret){
        OperationLog operLog = new OperationLog();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        operLog.setStartTime(System.currentTimeMillis());
        operLog.setUrl(request.getRequestURI().toString());
        operLog.setMethod(request.getMethod());

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        LoggerAnnotation loggerAnnotation = method.getAnnotation(LoggerAnnotation.class);
        if (loggerAnnotation != null){
            operLog.setSource(loggerAnnotation.operSource());
            operLog.setOperName(loggerAnnotation.operName());
            operLog.setSeverity(loggerAnnotation.severity());
            operLog.setPersistence(loggerAnnotation.isPersistence());
        }
        operLog.setRequestMethod(signature.getDeclaringTypeName() + "." + signature.getName());
        operLog.setRemoteAddr(request.getRemoteAddr());
        operLog.setArgs(writeValueAsSting(request.getParameterMap()));
        UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
        operLog.setUserAgent(userAgent);
        operLog.setEndTime(System.currentTimeMillis());
        operLog.setResult(ret);
        return operLog;
    }
    @Around("logger()")
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable{
        Object ret = null;
        try{
            ret = pjp.proceed();
            OperationLog operLog = buildOperationLog(pjp,ret);
            System.out.println(writeValueAsSting(operLog));
            log.info(writeValueAsSting(operLog));
        }catch (Throwable th){
            OperationLog operLog = buildOperationLog(pjp,ret);
            System.out.println(writeValueAsSting(operLog));
            log.info(writeValueAsSting(operLog));
        }
        return ret;
    }
    @AfterThrowing(value = "logger()",throwing = "throwable")
    public void doAfterThrowing(JoinPoint joinPoint,Throwable throwable){
        OperationLog operLog = buildExceptionOperationLog(joinPoint,throwable);
        System.out.println(writeValueAsSting(operLog));
        log.info(writeValueAsSting(operLog));
    }
    private OperationLog buildExceptionOperationLog(JoinPoint joinPoint,Throwable t){
        ExceptionOperLog operLog = new ExceptionOperLog(buildOperationLog(joinPoint,"null"));
        operLog.setExceptionName(t.getClass().getName());
        operLog.setExceptionMessage(stackTraceToString(t.getClass().getName(),t.getMessage(),t.getStackTrace()));
        return operLog;
    }
}
